/**
 * Copyright (C) 2008+ Spark Zheng
 *
 * @file	include/timer.h
 * @brief
 *      timer module include the operations of:
 *	-1. Wall time: (time_t <=> struct tm)
 *	-2. System time: struct timeval / clock (RTC, PIT, TSC)
 *	-3. Kernel jiffies (linux/time.h)
 *	-4. signal -- SIGALRM (struct timespec) / setitimer(getitimer)
 *	-5. sleep/usleep/nanosleep (struct timespec)
 *	-6. timer
 *
 *	if need more precision, think about __volatile__ __asm__("nop");
 *
 * @see		dlib
 * @module      dlib
 *
 * @history
 *      version 0.0.1   Spark Zheng init timer module
 */

#ifndef _DLIB_TIMER_H
#define _DLIB_TIMER_H

#ifdef __cplusplus
extern "C" {
#endif

#include "global.h"
#include "heap.h"

#include <sys/times.h>
#include <sys/time.h>
#include <time.h>
	
//extern unsigned long volatile jiffies;

/**
 * macros for struct timeval, usleep, gettimeofday...
 */
#ifndef DLIB_UTIMER_INIT
#define DLIB_UTIMER_INIT(tvp, sec, usec)	do { \
	(tvp)->tv_sec = sec; (tvp)->tv_usec = usec; } while (0)
#endif

#ifndef DLIB_UTIMER_RECORD
#define DLIB_UTIMER_RECORD(tvp)		gettimeofday(tvp, NULL)
#endif

#ifndef DLIB_UTIMER_IS_SET
#define DLIB_UTIMER_IS_SET(tvp)		((tvp)->tv_sec || (tvp)->tv_usec)
#endif

#ifndef DLIB_UTIMER_CLEAR
#define DLIB_UTIMER_CLEAR(tvp)		(tvp)->tv_sec = (tvp)->tv_usec = 0
#endif

#ifndef DLIB_UTIMER_CMP
#define DLIB_UTIMER_CMP(a, b, CMP) \
	(((a)->tv_sec == (b)->tv_sec) ? \
	((a)->tv_usec CMP (b)->tv_usec) : \
	((a)->tv_sec CMP (b)->tv_sec))
#endif

#ifndef DLIB_UTIMER_ADD
#define DLIB_UTIMER_ADD(a, b, result)	do { \
		(result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
		(result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
		if ((result)->tv_usec >= 1000000) { \
			++(result)->tv_sec; \
			(result)->tv_usec -= 1000000; \
		} } while (0)
#endif

#ifndef DLIB_UTIMER_SUB
#define DLIB_UTIMER_SUB(end, start, result) do { \
		(result)->tv_sec = (end)->tv_sec - (start)->tv_sec; \
		(result)->tv_usec = (end)->tv_usec - (start)->tv_usec; \
		if ((result)->tv_usec < 0) { \
			--(result)->tv_sec; \
			(result)->tv_usec += 1000000; \
		} } while (0)
#endif
/** end struct timeval */

/**
 * macros for struct timespec(nano), nanosleep...
 */
#ifndef DLIB_NTIMER_INIT
#define DLIB_NTIMER_INIT(tvp, sec, nsec)	do { \
		tvp->tv_sec = sec; tvp->tv_nsec = nsec; } while (0)
#endif

#ifndef DLIB_NTIMER_RECORD
#define DLIB_NTIMER_RECORD(tsp)		clock_gettime(clock(), tsp)
#endif

#ifndef DLIB_NTIMER_IS_SET
#define DLIB_NTIMER_IS_SET(tsp)		((tsp)->tv_sec || (tsp)->tv_nsec)
#endif

#ifndef DLIB_NTIMER_CLEAR
#define DLIB_NTIMER_CLEAR(tsp)		(tsp)->tv_sec = (tsp)->tv_nsec = 0
#endif

#ifndef DLIB_NTIMER_CMP
#define DLIB_NTIMER_CMP(a, b, CMP) \
	(((a)->tv_sec == (b)->tv_sec) ? \
	((a)->tv_nsec CMP (b)->tv_nsec) : \
	((a)->tv_sec CMP (b)->tv_sec))
#endif

#ifndef DLIB_NTIMER_ADD
#define DLIB_NTIMER_ADD(a, b, result)	do { \
	(result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
	(result)->tv_nsec = (a)->tv_nsec + (b)->tv_nsec; \
	if ((result)->tv_nsec >= 1000000000) { \
		++(result)->tv_sec; \
		(result)->tv_nsec -= 1000000000; \
	} } while (0)
#endif

#ifndef DLIB_NTIMER_SUB
#define DLIB_NTIMER_SUB(end, start, result)	do { \
	(result)->tv_sec = (end)->tv_sec - (start)->tv_sec; \
	(result)->tv_nsec = (end)->tv_nsec - (start)->tv_nsec; \
	if ((result)->tv_nsec < 0) { \
		--(result)->tv_sec; \
		(result)->tv_nsec += 1000000000; \
	} } while (0)
#endif

#ifndef DLIB_UTIMER_TO_NTIMER
#define DLIB_UTIMER_TO_NTIMER(tv, ts)	do { \
	(ts)->tv_sec = (tv)->tv_sec; \
	(ts)->tv_nsec = (tv)->tv_usec * 1000; \
	} while (0)
#endif

#ifndef DLIB_NTIMER_TO_UTIMER
#define DLIB_NTIMER_TO_UTIMER(tv, ts)	do { \
	(tv)->tv_sec = (ts)->tv_sec; \
	(tv)->tv_usec = (ts)->tv_nsec / 1000; \
	} while (0)
#endif
/** end struct timespec */

/** 
 * functions for time_t & struct tm
 */
/**
 * dlib_day_span
 * @brief calculate the days between from to to
 * @param from: from time
 * @param to: to time
 */
extern int dlib_day_span(struct tm *from, struct tm *to);

/**
 * dlib_day_is_between
 * @brief whether the day is between now to n days ago.
 * @param f: from time
 * @param t: to time, if to is NULL, use now
 * @param n: n days
 */
extern int dlib_day_is_between(struct tm *from, struct tm *to, int32_t n);



/** 
 * min heap based timer implementation
 * core structure, dlib_timer, dlib timer callback 
 * timer APIs:
 * -1. timer_init
 * -2. timer_exit
 * -3. timer_add
 * -4. timer_del
 * -5. timer_reset
 * -6. timer_timeout
 * -8. timer_run
 */

/**
 * dlib_timer_fn, timer running callback
 */
typedef int (*dlib_timer_fn)(void *data, struct timeval *tvp);

typedef struct dlib_timer	dlib_timer_t;

struct dlib_timer {

};

#ifdef __cplusplus
}
#endif
#endif
